 /*******************************************************************************
  * Copyright (c) 2000, 2005 IBM Corporation and others.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
  * http://www.eclipse.org/legal/epl-v10.html
  *
  * Contributors:
  * IBM Corporation - initial API and implementation
  *******************************************************************************/

 package org.eclipse.ui.internal.ide.dialogs;

 import java.io.IOException ;
 import java.io.InputStream ;
 import java.util.ArrayList ;

 import javax.xml.parsers.FactoryConfigurationError ;
 import javax.xml.parsers.ParserConfigurationException ;
 import javax.xml.parsers.SAXParser ;
 import javax.xml.parsers.SAXParserFactory ;

 import org.eclipse.core.runtime.IStatus;
 import org.eclipse.core.runtime.Status;
 import org.eclipse.ui.internal.ide.IDEWorkbenchMessages;
 import org.eclipse.ui.internal.ide.IDEWorkbenchPlugin;
 import org.xml.sax.Attributes ;
 import org.xml.sax.ContentHandler ;
 import org.xml.sax.InputSource ;
 import org.xml.sax.Locator ;
 import org.xml.sax.SAXException ;
 import org.xml.sax.helpers.DefaultHandler ;

 /**
  * A parser for the the welcome page
  */
 public class WelcomeParser extends DefaultHandler {
     private static final String TAG_WELCOME_PAGE = "welcomePage"; //$NON-NLS-1$

     private static final String TAG_INTRO = "intro"; //$NON-NLS-1$

     private static final String TAG_ITEM = "item"; //$NON-NLS-1$

     private static final String TAG_BOLD = "b"; //$NON-NLS-1$

     private static final String TAG_ACTION = "action"; //$NON-NLS-1$

     private static final String TAG_PARAGRAPH = "p"; //$NON-NLS-1$

     private static final String TAG_TOPIC = "topic"; //$NON-NLS-1$

     private static final String ATT_TITLE = "title"; //$NON-NLS-1$

     private static final String ATT_FORMAT = "format"; //$NON-NLS-1$

     private static final String ATT_PLUGIN_ID = "pluginId"; //$NON-NLS-1$

     private static final String ATT_CLASS = "class"; //$NON-NLS-1$

     private static final String ATT_ID = "id"; //$NON-NLS-1$

     private static final String ATT_HREF = "href"; //$NON-NLS-1$

     private static final String FORMAT_WRAP = "wrap"; //$NON-NLS-1$

     private static final char DELIMITER = '\n'; // sax parser replaces crlf with lf

     private SAXParser parser;

     private String title;

     private WelcomeItem introItem;

     private ArrayList items = new ArrayList ();

     private String format;

     private class WelcomeContentHandler implements ContentHandler {
         protected ContentHandler parent;

         public void setParent(ContentHandler p) {
             parent = p;
         }

         public void characters(char[] ch, int start, int length)
                 throws SAXException {
         }

         public void endDocument() throws SAXException {
         }

         public void endElement(String namespaceURI, String localName,
                 String qName) throws SAXException {
         }

         public void endPrefixMapping(String prefix) throws SAXException {
         }

         public void ignorableWhitespace(char[] ch, int start, int length)
                 throws SAXException {
         }

         public void processingInstruction(String target, String data)
                 throws SAXException {
         }

         public void setDocumentLocator(Locator locator) {
         }

         public void skippedEntity(String name) throws SAXException {
         }

         public void startDocument() throws SAXException {
         }

         public void startElement(String namespaceURI, String localName,
                 String qName, Attributes atts) throws SAXException {
         }

         public void startPrefixMapping(String prefix, String uri)
                 throws SAXException {
         }
     }

     private class WelcomePageHandler extends WelcomeContentHandler {
         public WelcomePageHandler(String newTitle) {
             title = newTitle;
         }

         public void startElement(String namespaceURI, String localName,
                 String qName, Attributes atts) throws SAXException {
             if (localName.equals(TAG_INTRO)) {
                 ItemHandler h = new IntroItemHandler();
                 h.setParent(WelcomePageHandler.this);
                 parser.getXMLReader().setContentHandler(h);
             } else if (localName.equals(TAG_ITEM)) {
                 ItemHandler h = new ItemHandler();
                 h.setParent(WelcomePageHandler.this);
                 parser.getXMLReader().setContentHandler(h);
             }
         }
     }

     private class ItemHandler extends WelcomeContentHandler {
         private ArrayList boldRanges = new ArrayList ();

         protected ArrayList wrapRanges = new ArrayList ();

         private ArrayList actionRanges = new ArrayList ();

         private ArrayList pluginIds = new ArrayList ();

         private ArrayList classes = new ArrayList ();

         private ArrayList helpRanges = new ArrayList ();

         private ArrayList helpIds = new ArrayList ();

         private ArrayList helpHrefs = new ArrayList ();

         private StringBuffer text = new StringBuffer ();

         protected int offset = 0;

         protected int textStart;

         protected int wrapStart;

         private class BoldHandler extends WelcomeContentHandler {
             public void characters(char[] ch, int start, int length)
                     throws SAXException {
                 ItemHandler.this.characters(ch, start, length);
             }

             public void endElement(String namespaceURI, String localName,
                     String qName) throws SAXException {
                 if (localName.equals(TAG_BOLD)) {
                     boldRanges.add(new int[] { textStart, offset - textStart });
                     parser.getXMLReader().setContentHandler(parent);
                 }
             }
         }

         private class ActionHandler extends WelcomeContentHandler {
             public ActionHandler(String pluginId, String className) {
                 pluginIds.add(pluginId);
                 classes.add(className);
             }

             public void characters(char[] ch, int start, int length)
                     throws SAXException {
                 ItemHandler.this.characters(ch, start, length);
             }

             public void endElement(String namespaceURI, String localName,
                     String qName) throws SAXException {
                 if (localName.equals(TAG_ACTION)) {
                     actionRanges
                             .add(new int[] { textStart, offset - textStart });
                     parser.getXMLReader().setContentHandler(parent);
                 }
             }
         }

         private class TopicHandler extends WelcomeContentHandler {
             public TopicHandler(String helpId, String href) {
                 helpIds.add(helpId);
                 helpHrefs.add(href);
             }

             public void characters(char[] ch, int start, int length)
                     throws SAXException {
                 ItemHandler.this.characters(ch, start, length);
             }

             public void endElement(String namespaceURI, String localName,
                     String qName) throws SAXException {
                 if (localName.equals(TAG_TOPIC)) {
                     helpRanges.add(new int[] { textStart, offset - textStart });
                     parser.getXMLReader().setContentHandler(parent);
                 }
             }
         }

         protected WelcomeItem constructWelcomeItem() {
             if (isFormatWrapped()) {
                 // replace all line delimiters with a space
 for (int i = 0; i < wrapRanges.size(); i++) {
                     int[] range = (int[]) wrapRanges.get(i);
                     int start = range[0];
                     int length = range[1];
                     for (int j = start; j < start + length; j++) {
                         char ch = text.charAt(j);
                         if (ch == DELIMITER) {
                             text.replace(j, j + 1, " "); //$NON-NLS-1$
 }
                     }
                 }
             }
             return new WelcomeItem(
                     text.toString(),
                     (int[][]) boldRanges.toArray(new int[boldRanges.size()][2]),
                     (int[][]) actionRanges
                             .toArray(new int[actionRanges.size()][2]),
                     (String []) pluginIds.toArray(new String [pluginIds.size()]),
                     (String []) classes.toArray(new String [classes.size()]),
                     (int[][]) helpRanges.toArray(new int[helpRanges.size()][2]),
                     (String []) helpIds.toArray(new String [helpIds.size()]),
                     (String []) helpHrefs.toArray(new String [helpHrefs.size()]));
         }

         public void characters(char[] ch, int start, int length)
                 throws SAXException {
             for (int i = 0; i < length; i++) {
                 text.append(ch[start + i]);
             }
             offset += length;
         }

         public void startElement(String namespaceURI, String localName,
                 String qName, Attributes atts) throws SAXException {
             textStart = offset;
             if (localName.equals(TAG_BOLD)) {
                 BoldHandler h = new BoldHandler();
                 h.setParent(ItemHandler.this);
                 parser.getXMLReader().setContentHandler(h);
             } else if (localName.equals(TAG_ACTION)) {
                 ActionHandler h = new ActionHandler(atts
                         .getValue(ATT_PLUGIN_ID), atts.getValue(ATT_CLASS));
                 h.setParent(ItemHandler.this);
                 parser.getXMLReader().setContentHandler(h);
             } else if (localName.equals(TAG_PARAGRAPH)) {
                 wrapStart = textStart;
             } else if (localName.equals(TAG_TOPIC)) {
                 TopicHandler h = new TopicHandler(atts.getValue(ATT_ID), atts
                         .getValue(ATT_HREF));
                 h.setParent(ItemHandler.this);
                 parser.getXMLReader().setContentHandler(h);
             }
         }

         public void endElement(String namespaceURI, String localName,
                 String qName) throws SAXException {
             if (localName.equals(TAG_ITEM)) {
                 items.add(constructWelcomeItem());
                 parser.getXMLReader().setContentHandler(parent);
             } else if (localName.equals(TAG_PARAGRAPH)) {
                 wrapRanges.add(new int[] { wrapStart, offset - wrapStart });
             }
         }
     }

     private class IntroItemHandler extends ItemHandler {
         public void endElement(String namespaceURI, String localName,
                 String qName) throws SAXException {
             if (localName.equals(TAG_INTRO)) {
                 introItem = constructWelcomeItem();
                 parser.getXMLReader().setContentHandler(parent);
             } else if (localName.equals(TAG_PARAGRAPH)) {
                 wrapRanges.add(new int[] { wrapStart, offset - wrapStart });
             }
         }
     }

     /**
      * Creates a new welcome parser.
      */
     public WelcomeParser() throws ParserConfigurationException , SAXException ,
             FactoryConfigurationError {
         super();
         SAXParserFactory factory = SAXParserFactory.newInstance();
         factory.setFeature("http://xml.org/sax/features/namespaces", true); //$NON-NLS-1$
 parser = factory.newSAXParser();

         parser.getXMLReader().setContentHandler(this);
         parser.getXMLReader().setDTDHandler(this);
         parser.getXMLReader().setEntityResolver(this);
         parser.getXMLReader().setErrorHandler(this);
     }

     /**
      * Returns the intro item.
      */
     public WelcomeItem getIntroItem() {
         return introItem;
     }

     /**
      * Returns the items.
      */
     public WelcomeItem[] getItems() {
         return (WelcomeItem[]) items.toArray(new WelcomeItem[items.size()]);
     }

     /**
      * Returns the title
      */
     public String getTitle() {
         return title;
     }

     /**
      * Returns whether or not the welcome editor input should be wrapped.
      */
     public boolean isFormatWrapped() {
         return FORMAT_WRAP.equals(format);
     }

     /**
      * Parse the contents of the input stream
      */
     public void parse(InputStream is) {
         try {
             parser.parse(new InputSource (is), this);
         } catch (SAXException e) {
             IStatus status = new Status(IStatus.ERROR,
                     IDEWorkbenchPlugin.IDE_WORKBENCH, 1, IDEWorkbenchMessages.WelcomeParser_parseException, e);
             IDEWorkbenchPlugin.log(IDEWorkbenchMessages.WelcomeParser_parseError, status);
         } catch (IOException e) {
             IStatus status = new Status(IStatus.ERROR,
                     IDEWorkbenchPlugin.IDE_WORKBENCH, 1, IDEWorkbenchMessages.WelcomeParser_parseException, e);
             IDEWorkbenchPlugin.log(IDEWorkbenchMessages.WelcomeParser_parseError, status);
         }
     }

     /**
      * Handles the start element
      */
     public void startElement(String namespaceURI, String localName,
             String qName, Attributes atts) throws SAXException {
         if (localName.equals(TAG_WELCOME_PAGE)) {
             WelcomeContentHandler h = new WelcomePageHandler(atts
                     .getValue(ATT_TITLE));
             format = atts.getValue(ATT_FORMAT);
             h.setParent(this);
             parser.getXMLReader().setContentHandler(h);
         }
     }
 }

